package com.dd.service.impl;
import ch.qos.logback.core.util.TimeUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dd.common.fileConstants.FileUploadStatus;
import com.dd.pojo.query.UserFileQuery;
import com.dd.pojo.query.DeleteFileQuery;
import com.dd.pojo.query.DeleteFolderQuery;
import com.dd.common.response.ResultCode;
import com.dd.common.compoents.MinioTemplate;
import com.dd.config.MinioConfig;
import com.dd.pojo.entry.File;
import com.dd.pojo.entry.Folder;
import com.dd.pojo.entry.UserFoldFile;
import com.dd.common.expection.BizException;
import com.dd.mapper.FileMapper;
import com.dd.mapper.FolderMapper;
import com.dd.mapper.UserFoldFileMapper;
import com.dd.pojo.vo.MergerFileVO;
import com.dd.service.FileService;
import com.dd.common.strategy.FileStrategyManage;
import com.dd.common.utils.FileUtil;
import com.dd.common.utils.ShortIdWorker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.dd.common.constans.RedisFileKey.PREFIX_RECYCLE;


@Service
@RequiredArgsConstructor
@Slf4j
public class FileServiceImpl implements FileService{


    private final FileMapper fileMapper;

    private final FolderMapper folderMapper;


    private final MinioTemplate minioTemplate;


    private final MinioConfig minioConfig;


    private final UserFoldFileMapper userFoldFileMapper;


    private final FileStrategyManage  fileStrategyManage;

    private  final RedisTemplate redisTemplate;


    /**
     * 文件夹的创建
     * @param folderName 文件夹名称
     * @param userId 用户ID
     * @param parentId 父文件夹ID
     */
    @Override
    @Transactional
    public void mkdirFolDer(String folderName,String userId,String parentId) {
          String foldId=ShortIdWorker.getInstance().nextId();
        folderMapper.insert(Folder.builder().folderId(foldId).folderName(folderName).parentID(parentId).uploadTime(LocalDateTime.now()).build());
        userFoldFileMapper.insert(UserFoldFile.builder().userId(userId).folderId(foldId).build());
    }

    /**
     * 上传文件
     * @param file 文件
     * @param fileMa5 文件md5
     * @param userId 用户ID
     * @param folderId 文件夹ID
     */
    @Override
    @Transactional
    public void upladFile(MultipartFile file, String fileMa5, String userId, String folderId) {
        String originalFilename = file.getOriginalFilename();

        String suffic = originalFilename.substring(originalFilename.lastIndexOf("."));

        Integer fileType= FileUtil.getFileType(suffic.substring(1,suffic.length()));
        try {
          fileMapper.insert(File.builder().fileMd5(fileMa5).filePath(minioConfig.getEndpoint()+"/"+minioConfig.getBucketName()+"/"+fileMa5+suffic).fileType(fileType).build());
           userFoldFileMapper.insert(UserFoldFile.builder().userId(userId).fileName(file.getOriginalFilename()).fileId(fileMa5).filePath(minioConfig.getEndpoint()+"/"+minioConfig.getBucketName()+"/"+fileMa5+suffic).fileUploadTime(LocalDateTime.now()).folderId(folderId).fileType(fileType).build());
          minioTemplate.uploadFile(minioConfig.getBucketName(),fileMa5+suffic,file,file.getContentType());

        } catch (Exception e) {
            log.error("error--{}",e.getMessage());
            throw new BizException(ResultCode.FILE_UPLOAD_ERROR);
        }

    }


    @Override
    public List<UserFoldFile> findFiles(String userId, String folderId) {

        LambdaQueryWrapper<UserFoldFile> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(UserFoldFile::getUserId,userId).eq(UserFoldFile::getFolderId,folderId);
        List<UserFoldFile> userFoldFiles = userFoldFileMapper.selectList(wrapper);
        List<UserFoldFile> newList = userFoldFiles.stream().filter(item -> item.getFileId() != null).collect(Collectors.toList());
        return newList;
    }

    @Override
    public List<Folder> findOneTypeFolder(String parentId, String userID) {

        List<Folder> folders = folderMapper.findOneTypeFolder(parentId, userID);
        return folders;
    }

    @Override
    public Folder findFolderInfo(String folderId,String userId) {

        Folder folderInfo = folderMapper.findFolderInfo(folderId, userId);
        return folderInfo ;
    }

    /**
     *
     *设置过期时间  过期以后将minio数据删除
     * @param deleteFileParams
     *   key uid  profix
     *   hashmap  md5  data
     * 全删除  删除mssql  删除minio
     */
    @Override
    @Transactional
    public void deleteFile(DeleteFileQuery deleteFileParams) {

     try {
         LambdaQueryWrapper<UserFoldFile> wrapper1 = new LambdaQueryWrapper<>();
         wrapper1.eq(UserFoldFile::getUserId,deleteFileParams.getUserId())
                 .eq(UserFoldFile::getFileId,deleteFileParams.getFileMd5())
                 .eq(UserFoldFile::getFileName,deleteFileParams.getFileName());

         List<UserFoldFile> userFoldFiles = userFoldFileMapper.selectList(wrapper1);
         if(!CollectionUtils.isEmpty(userFoldFiles)){

             String str = JSONUtil.toJsonStr(userFoldFiles.get(0));
             String Key = PREFIX_RECYCLE+userFoldFiles.get(0).getUserId();
             Boolean b = redisTemplate.opsForHash().hasKey(Key, deleteFileParams.getFileMd5());
             if(!b){
                 redisTemplate.opsForHash().put(Key,deleteFileParams.getFileMd5(),str);
                 redisTemplate.expire(Key,30, TimeUnit.DAYS);
             }
         }

         userFoldFileMapper.delete(wrapper1);
     }
     catch (Exception e){

         log.error("error->{}",e.getMessage());
     }

    }

    @Override
    @Transactional
    public void deleteFolder(DeleteFolderQuery deleteFolderParams) {
        //将删除文件夹数据删除
        LambdaQueryWrapper<Folder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Folder::getFolderId,deleteFolderParams.getCurrentFolderId());
        folderMapper.delete(wrapper);
        //将属于删除文件夹的文件和文件夹的父ID更改为当前文件夹的父ID

        LambdaUpdateWrapper<Folder> folderLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        folderLambdaUpdateWrapper.set(Folder::getParentID,deleteFolderParams.getFolderParentId()).eq(Folder::getParentID,deleteFolderParams.getCurrentFolderId());
        folderMapper.update(null,folderLambdaUpdateWrapper);

        LambdaUpdateWrapper<UserFoldFile> userFoldFileLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userFoldFileLambdaUpdateWrapper.set(UserFoldFile::getFolderId,deleteFolderParams.getFolderParentId())
                .eq(UserFoldFile::getFolderId,deleteFolderParams.getCurrentFolderId());
        userFoldFileMapper.update(null,userFoldFileLambdaUpdateWrapper);

    }


    /**
     * 检查文件上传状态
     * @param checks
     * @return
     */
    @Override
    public int checkFileUplaodStatus(UserFileQuery checks) {
        boolean status = fileStrategyManage.checkFile(checks);
        return status? FileUploadStatus.UPLOAD_FILE_OVER:FileUploadStatus .UPLOAD_FILE_NOT;
    }

    @Override

    public void uploadChunk(String fileChunkIndex, String fileMd5, MultipartFile file) {



        try {
            boolean exist = minioTemplate.doesFolderExist(fileMd5);
            if(!exist){
                minioTemplate.mkdirFolder(fileMd5);
            }
            minioTemplate.uploadFile(minioConfig.getBucketName(),fileMd5+"/"+fileChunkIndex,file);
        }
        catch (Exception e){
            log.error("上传分片文件失败--{}",e.getMessage());
            throw new BizException("上传分片文件失败");
        }


    }

    @Override
    public List<Integer> findChunkd(String fileMd5) {
        List<Integer> list = minioTemplate.chunksNameList(fileMd5);
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void merger(MergerFileVO vo,String uid) {
        String fileName=vo.getFileMd5()+"."+vo.getFileSuffix();
        minioTemplate.merger(vo.getFileMd5(),fileName);
        String path=minioConfig.getEndpoint()+"/"+minioConfig.getBucketName()+"/"+fileName;
        Integer fileType = FileUtil.getFileType(vo.getFileSuffix());
        //入库
        fileMapper.insert(File.builder().fileMd5(vo.getFileMd5()).filePath(path).fileType(fileType).build());
        userFoldFileMapper.insert(UserFoldFile.builder().userId(uid).fileName(vo.getFileName()).fileId(vo.getFileMd5()).filePath(path).fileUploadTime(LocalDateTime.now()).fileType(fileType).folderId("").build());
        //清理内存碎片
        minioTemplate.deleteObject(vo.getFileMd5());
    }

    @Override
    @Async("fileAsyncThreadPool")
    public Future<Integer> uploadFile(Integer index) throws InterruptedException {

        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"--"+index);
        return new AsyncResult(index);
    }


}




